package com.ctrip.corp.obt.shard.context;

import com.ctrip.corp.obt.generic.core.context.RequestContext;
import com.ctrip.corp.obt.generic.utils.Assert;
import com.ctrip.corp.obt.generic.utils.ExceptionUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.shard.holder.PageHolder;
import com.ctrip.corp.obt.shard.sql.mybatis.interceptor.pagination.IPage;
import com.ctrip.corp.obt.shard.sql.mybatis.interceptor.pagination.Page;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

import static com.ctrip.corp.obt.shard.configuration.DataShardProperties.currentParamName;
import static com.ctrip.corp.obt.shard.configuration.DataShardProperties.sizeParamName;

public class PageContext {

    public static final String PAGE = "page";

    private static Integer getCurrent() {
        String current = RequestContext.getCurrentContext().getRequest().getParameter(currentParamName());
        Assert.notNull(current, "Parameter [current] must be set while doing page");
        return Integer.parseInt(current);
    }

    private static Integer getSize() {
        String size = RequestContext.getCurrentContext().getRequest().getParameter(sizeParamName());
        Assert.notNull(size, "Parameter [size] must be set while doing page");
        return Integer.parseInt(size);
    }

    public static boolean hasPageParam() {
        String current = RequestContext.getCurrentContext().getRequest().getParameter(currentParamName());
        String size = RequestContext.getCurrentContext().getRequest().getParameter(sizeParamName());
        return StringUtils.isNotBlank(current) && StringUtils.isNotBlank(size);
    }

    public static Page continueBuildPageWithPageQueryCondition(String mappedStatement) {
        Page<?> page = (Page<?>)PageContext.getPage();
        if (null == page) {
            return null;
        }
        PageHolder.PageQueryCache pageQuery = PageHolder.getPageQuery(mappedStatement);
        if (pageQuery != null) {
            page.setOptimizeCountSql(pageQuery.isOptimizeCountSql());
            page.setSearchCount(pageQuery.isSearchCount());
            page.setMaxLimit(pageQuery.getMaxLimit());
            page.setOptimizeJoinOfCountSql(pageQuery.isOptimizeJoinOfCountSql());
            page.setCountId(pageQuery.getCountId());
        }
        return page;
    }

    public static <T> IPage<T> buildPage() {
        Page<T> page = new Page<>();
        page.setCurrent(getCurrent());
        page.setSize(getSize());
        return page;
    }

    public static void setPage(IPage<?> page) {
        Assert.notNull(RequestContext.getCurrentContext().getRequest(), "Wrongly context, no request was found in this context, may be the current thread is not wrapped by ttl");
//        RequestContext.getCurrentContext().addContextParams(PAGE, JsonUtils.toJsonString(page));
        RequestContext.getCurrentContext().addContextParams(PAGE, page);
        RequestContext.getCurrentContext().getRequest().setAttribute(PAGE, page);
    }

    @SuppressWarnings("unchecked")
    public static <T> IPage<T> startPage(int current, int size) {
        IPage<T> page = (IPage<T>) Optional.ofNullable(getPage()).orElse(new Page<>());
        page.setCurrent(current);
        page.setSize(size);
        setPage(page);
        return page;
    }

    @SuppressWarnings("unchecked")
    public static <T> IPage<T> startPage(int current, int size, Supplier func) {
        if (Objects.isNull(RequestContext.getCurrentContext().getRequest())) {
            throw ExceptionUtils.mpe("Wrongly context, no request was found in this context, may be the current thread is not wrapped by ttl");
        }
        IPage<T> page = (IPage<T>) Optional.ofNullable(getPage()).orElse(new Page<>());
        page.setCurrent(current);
        page.setSize(size);
        setPage(page);
        try {
            func.get();
        } finally {
            Optional.ofNullable(RequestContext.getCurrentContext().getRequest()).ifPresent(request -> request.removeAttribute(PAGE));
            RequestContext.getCurrentContext().remove(PAGE);
        }
        return page;
    }


    public static IPage<?> getPage() {
        if (Objects.isNull(RequestContext.getCurrentContext().getRequest())) {
//            throw ExceptionUtils.mpe("Wrongly context, no page was found in this context, may be the current thread is not wrapped by ttl");
            return null;
        }
        return (IPage<?>) Optional.ofNullable(RequestContext.getCurrentContext().getContextParams(PAGE))
//                .map(Object::toString).map(pageStr -> JsonUtils.parse(pageStr, Page.class))
                .orElse(RequestContext.getCurrentContext().getRequest().getAttribute(PAGE));
//        return JsonUtils.parse(RequestContext.getCurrentContext().getContextParams(PAGE).toString(), Page.class);
//        return (IPage<?>) RequestContext.getCurrentContext().getRequest().getAttribute(PAGE);
    }

    public static IPage<?> getPage(HttpServletRequest request) {
        return (IPage<?>) request.getAttribute(PageContext.PAGE);
    }

    public static void unset() {
        Optional.ofNullable(RequestContext.getCurrentContext().getRequest()).ifPresent(request -> request.removeAttribute(PAGE));
    }

    private PageContext() {}
}
